/**
 * 
 */
package org.hotpotmaterial.codegenn2.aspect;

import java.lang.annotation.Annotation;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hotpotmaterial.codegenn2.common.HmLogConstants;
import org.hotpotmaterial.codegenn2.config.bean.LogConfig;
import org.hotpotmaterial.codegenn2.utils.RandomUtil;
import org.hotpotmaterial.codegenn2.utils.RequestUtil;
import org.slf4j.Logger;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * @author cakydeveloper
 *
 */
@Aspect
public class ControllerAspect {

	// 访问日志
	private static Logger accessLogger = LogConfig.getLogger(HmLogConstants.ACCESS_LOGNAME);
	// 异常日志
	private static Logger exceptLogger = LogConfig.getLogger(HmLogConstants.EXEC_LOGNAME);
	// 参数日志
	private static Logger paramLogger = LogConfig.getLogger(HmLogConstants.PARAM_LOGNAME);
	// 返回结果日志
	private static Logger resultLogger = LogConfig.getLogger(HmLogConstants.RESULT_LOGNAME);
	// json转换
	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

	/**
	 * 切入点
	 */
	@Pointcut("within(@org.springframework.web.bind.annotation.RestController *) ||"
			+ "within(@org.springframework.stereotype.Controller *)")
	public void allPublicControllerMethodsPointcut() {
	}

	@Around("allPublicControllerMethodsPointcut()")
	public Object preceedAndLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		Object result = null;
		// 生成请求tag
		String tag = RandomUtil.getRandom(16, RandomUtil.TYPE.LETTER_CAPITAL_NUMBER);
		try {
			// 获取请求访问日志
			accessLogger.info(RequestUtil.getRequestInfo(tag));
			// 方法执行日志
			paramLogger.info(methodExecInfo(proceedingJoinPoint, tag));
		} catch (Exception e) {
			exceptLogger.error("Exception occurred before executing method", e);
		}

		// 计时开始
		StopWatch timer = new StopWatch();
		// 执行方法
		try {
			timer.start();
			result = proceedingJoinPoint.proceed();
			resultLogger.info(methodResultInfo(timer, result, tag));
		} catch (Throwable e) {
			exceptLogger.error("Exception occurred during executing method", e);
		}

		return result;
	}

	/**
	 * 方法执行起始日志
	 * 
	 * @param proceedingJoinPoint
	 * @return
	 */
	private String methodExecInfo(ProceedingJoinPoint proceedingJoinPoint, String reqtag) {
		StringBuilder sb = new StringBuilder();
		// 类
		String classInfo = proceedingJoinPoint.getTarget().getClass().getName();
		sb.append("class: [").append(classInfo).append("]");
		// 方法
		MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
		sb.append(", method: [").append(methodSignature.getName()).append("]");
		// 参数
		Object argValues[] = proceedingJoinPoint.getArgs();
		// 参数名
		String argNames[] = methodSignature.getParameterNames();
		// 参数注解
		Annotation annotations[][] = methodSignature.getMethod().getParameterAnnotations();
		sb.append(", params: [");
		if (argNames.length > 0) {
			for (int i = 0, length = argNames.length; i < length; ++i) {
				boolean needsSerialization = false;
				// 如果是resuestbody的参数可以转为json
				sb.append(argNames[i]).append(": [");
				for (Annotation annotation : annotations[i]) {
					if (annotation instanceof RequestBody) {
						needsSerialization = true;
						break;
					}
				}
				// 转为string
				if (!(argValues[i] instanceof HttpServletRequest) && !(argValues[i] instanceof HttpServletResponse)) {
					if (argValues[i] instanceof ByteArrayResource) {
						// 如果是byte
						sb.append("file_size=").append(((ByteArrayResource) argValues[i]).contentLength());
					} else if (argValues[i] instanceof MultipartFile) {
						// 如果是文件
						sb.append("file_size=").append(((MultipartFile) argValues[i]).getSize());
					} else {
						// 普通参数
						if (needsSerialization) {
							sb.append(toJson(argValues[i]));
						} else {
							sb.append(argValues[i]);
						}
					}
				}
				sb.append("]").append(i == (length - 1) ? "" : ", ");
			}
		}
		sb.append("]").append(" >>>> ").append(reqtag);

		return sb.toString();
	}

	/**
	 * 方法执行结束日志
	 * 
	 * @return
	 */
	public String methodResultInfo(StopWatch timer, Object result, String reqtag) {
		StringBuilder sb = new StringBuilder();
		// 运行时间
		timer.stop();
		sb.append("execution_time: [").append(timer.getTotalTimeMillis()).append("ms]");
		// 结果
		sb.append(", return_value: [");
		if (result instanceof ResponseEntity) {
			// 如果是response entity
			ResponseEntity<?> tmp = (ResponseEntity<?>) result;
			if (tmp.getBody().getClass().isArray()) {
				sb.append("result is array");
			} else if (null == tmp.getBody() || !(tmp.getBody() instanceof InputStreamResource)) {
				sb.append(toJson(tmp.getBody()));
			}
		} else if (result instanceof ByteArrayResource) {
			// 如果是byte
			sb.append("file_size=").append(((ByteArrayResource) result).contentLength());
		} else if (result instanceof MultipartFile) {
			// 如果是文件
			sb.append("file_size=").append(((MultipartFile) result).getSize());
		} else if (null == result) {
			sb.append("result is null");
		} else {
			sb.append(result.toString());
		}
		sb.append("]").append(" >>>> ").append(reqtag);

		return sb.toString();
	}

	/**
	 * 转换为json
	 * 
	 * @param object
	 * @return
	 */
	private static String toJson(Object object) {
		try {
			return object == null ? "null" : OBJECT_MAPPER.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			exceptLogger.error("json convert exception", e);
		}

		return null;
	}

}
